package com.huangfu.learning.javabase.java.pattern.create.prototype;

import java.io.*;

/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/8/7
 * Time: 19:25
 * Version: 1.0
 * Description: 原型模式：用原型实例指定创建对象的种类，并且通过拷贝这些原型创建新的对象。--拷贝
 * https://www.cnblogs.com/xiaobai1226/p/8488332.html
 * https://www.cnblogs.com/lwbqqyumidi/p/3746821.html
 * <p>
 * 原型模式的特点：
 * 1、由原型对象自身创建目标对象。也就是说，对象创建这一动作发自原型对象本身。
 * 2、目标对象是原型对象的一个克隆。也就是说，通过原型模式创建的对象，不仅仅与原型对象具有相同的结构，还与原型对象具有相同的值。
 * 3、根据对象克隆深度层次的不同，有浅度克隆与深度克隆。
 *
 * 优点： 1、性能提高。 2、逃避构造函数的约束。
 * 缺点： 1、配备克隆方法需要对类的功能进行通盘考虑，这对于全新的类不是很难，但对于已有的类不一定很容易，
 *      特别当一个类引用不支持串行化的间接对象，或者引用含有循环结构的时候。
 *      2、必须实现 Cloneable 接口。
 **/
public class PrototypePattern {

    // 测试
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 浅克隆测试
        Book book = new Book();
        Author author = new Author();
        author.setName("corn");
        author.setAge(100);
        book.setAuthor(author);
        book.setTitle("好记性不如烂博客");
        book.setPageNum(230);

        System.out.println("book1:" + book);
        Book book1 = book.clone();
        System.out.println("book2:" + book1);

        System.out.println(book == book1);  // false
        System.out.println(book.getPageNum() == book1.getPageNum());   // true
        System.out.println(book.getTitle() == book1.getTitle());        // true
        System.out.println(book.getAuthor() == book1.getAuthor());        // true
        // 结论：虽然复制出来的对象重新在堆上开辟了内存空间，但是，对象中各属性确保持相等。
        // 对于基本数据类型很好理解，但对于引用数据类型来说，则意味着此引用类型的属性所指向的对象本身是相同的， 并没有重新开辟内存空间存储。
        // 换句话说，引用类型的属性所指向的对象并没有复制。

        /**************************深度复制***********************************/

        Book2 book2 = new Book2();
        Author2 author2 = new Author2();
        author2.setName("corn");
        author2.setAge(100);
        book2.setAuthor2(author2);
        book2.setTitle("好记性不如烂博客");
        book2.setPageNum(230);

        System.out.println("book2：" + book2);
        Book2 book3 = book2.deepClone();
        System.out.println("book3：" + book3);

        System.out.println(book2 == book3);  // false
        System.out.println(book2.getPageNum() == book3.getPageNum());   // true
        System.out.println(book2.getTitle() == book3.getTitle());        // false
        System.out.println(book2.getAuthor2() == book3.getAuthor2());        // false
        //结论:深复制不仅在堆内存上开辟了空间以存储复制出的对象，甚至连对象中的引用类型的属性所指向的对象也得以复制，重新开辟了堆空间存储。
    }
}

class Author {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Author{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

/**
 * 浅克隆
 * 实现Cloneable接口
 * 重写Object的clone
 */
class Book implements Cloneable {

    private String title;
    private int pageNum;
    private Author author;

    // 浅克隆
    public Book clone() {
        Book book = null;
        try {
            book = (Book) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return book;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", pageNum=" + pageNum +
                ", author=" + author +
                '}';
    }
}

/**
 * 深复制
 * 深复制一般是通过对象的序列化和反序列化得以实现。序列化时，需要实现Serializable接口
 */
class Author2 implements Serializable {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Author2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

class Book2 implements Serializable {

    private String title;
    private int pageNum;
    private Author2 author2;

    public Book2 deepClone() throws IOException, ClassNotFoundException {

        // 写入当前对象的二进制流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 读出二进制流产生的新对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Book2) ois.readObject();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public Author2 getAuthor2() {
        return author2;
    }

    public void setAuthor2(Author2 author2) {
        this.author2 = author2;
    }

    @Override
    public String toString() {
        return "Book2{" +
                "title='" + title + '\'' +
                ", pageNum=" + pageNum +
                ", author2=" + author2 +
                '}';
    }
}